对比Web开发和原生开发,通常APP被说到的优势在于两点:1.交互体验更流畅。2.硬件支持更友好。随着HTML5的发展,我们可以通过浏览器做的事情会越来越多,之前只能原生开发做的事情通过浏览器也能实现,比如下面我要说的"视频录制"。
不啰嗦,先看demo:一张canvas画布上有一颗红色的小球左右来回循环滚动,点击"开始录制"按钮,视频就开始录制,再点击"下载录像"按钮,视频结束录制,并且会下载一个webm格式的视频文件在本地。
我这里分享一整个前端录制+服务端存储+下载的完整方案(上面demo是简版,无服务端存储步骤)。整个开发流程可分为3个步骤:1.视频录制,2.上传持久化存储,3.下载视频。整个前端流程可概括成下图
下面详细地介绍各实现步骤
首先,调研找到了HTML5 的一个API MediaRecorder 适合来做录制这个事情,摘抄下官网文档:
MediaRecorder 是 MediaStream Recording API 提供的用来进行媒体轻松录制的接口, 他需要通过调用 MediaRecorder() 构造方法进行实例化。 一个新的MediaRecorder对象,对指定的MediaStream 对象进行录制,支持的配置项包括设置容器的MIME 类型 (例如"video/webm" 或者 "video/mp4")和音频及视频的码率或者二者同用一个码率
通过上面介绍可以得知,只要能得到录制对象的 MediaStream (本文展示的是canvas录制,目前能转为MediaStream的对象还有video,audio,电脑前置摄像头输入)就可以轻松的实现录制这个事情。
接着找到canvas如下API,可用于从canvas中获取到我们需要的MediaStream
MediaStream = canvas.captureStream(frameRate);
以上就走通了视频的录制过程:canvas —>MediaStream—>MediaRecord—>视频。整个录制的示例代码如下:
//录制到的数据数据
letallChunks=[];
letcanvas=document.getElementById("canvasId");
letstream=canvas.captureStream(60); // 60 FPS recording
letrecorder=newMediaRecorder(stream, {
mimeType: 'video/webm;codecs=vp9',
});
// canvas 录制回调
recorder.ondataavailable=e=>{
allChunks.push(e.data);
}
recorder.start(10);
还有一点要注意的是我们这边录制视频的格式是webm, MP4格式的在目前最新chrome版本中暂不支持,可通过下面API查询支持情况
MediaRecorder.isTypeSupported('video/webm'); //true
MediaRecorder.isTypeSupported('video/mp4');//false
上传要做的事情相对来说比较明确,只需要将上面提到的allChunks对象上传即可。我这边在业务上考虑到了两个点:1.长时间录制allChunks尺寸会过大,上传会比较耗时。2.录制过程中如有突发情况(刷新或点击链接跳走),视频就无法保存了。
针对于上面两种业务场景,我简单的采用了Blob切片上传,开始录制后,设置一个定时器,每隔10秒发送增量的视频切片给后端,前端示例代码如下:
// 定时向后端发送 增量blob
function sendNewChunks(){
let start = 0;
let iterationIndex = 0;
sendTimer = window.setInterval( () => {
let allBlob = new Blob(allChunks);
// 把增量视频的片段切下来
let newBlob = allBlob.slice(start, allBlob.size);
start = allBlob.size;
// 当录像有更新的时候,才向接口发送分片信息
if (newBlob.size > 0) {
iterationIndex++;
// 在这里Http post 发送newBlob对象
}
},10000)
}
我这个场景,后端用是node.js,所以在node层直接调用阿里云的OSS API 进行上传即可:
letresult=awaitclient.put('object-name', newBuffer('hello world'));
这边一点需要注意的是,OSS node.js API 这边需要上传的是Buffer格式的数据,在尝试了各种方法后,我最终能走通整个流程做法是:先在前端把Blob格式的视频切片转换成Uint8Array,传递到node.js后再通过new Buffer.from(dataArray)转换成Buffer格式。
下载视频方案用的是通过websocket向前端不断的推小的视频切片,再由前端把视频拼接起来,借助浏览器转码下载。
// 通过blob对象下载
function downloadByBlob(blobObj) {
const link = document.createElement('a');
link.style.display = 'none';
const downloadUrl = window.URL.createObjectURL(blobObj);
link.href = downloadUrl;
link.download = `test.webm`;
document.body.appendChild(link);
link.click();
link.remove();
}
最后 实践过程中,有查阅到的一些相关知识点我在下面列一下,希望对你有所帮助! 相关知识点
目的:这些对象原始的设计目的,与WebGL项目有关。所谓WebGL,就是指浏览器与显卡之间的通信接口,为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个32位整数,两端的JavaScript脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像C语言那样,直接操作字节,将4个字节的32位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。
兼容性相关